﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.MountAndBlade.View.MissionViews.MissionAgentLabelView
// Assembly: TaleWorlds.MountAndBlade.View, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 61152EB8-513B-4D4A-8AC0-0D1465C6CF6C
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\Modules\Native\bin\Win64_Shipping_Client\TaleWorlds.MountAndBlade.View.dll

using System;
using System.Collections.Generic;
using System.ComponentModel;
using TaleWorlds.Core;
using TaleWorlds.DotNet;
using TaleWorlds.Engine;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade.View.Screens;

#nullable disable
namespace TaleWorlds.MountAndBlade.View.MissionViews
{
  public class MissionAgentLabelView : MissionView
  {
    private const float _highlightedLabelScaleFactor = 30f;
    private const float _labelBannerWidth = 0.4f;
    private const float _labelBlackBorderWidth = 0.44f;
    private readonly Dictionary<Agent, MetaMesh> _agentMeshes;
    private readonly Dictionary<Texture, Material> _labelMaterials;
    private bool _wasOrderScreenVisible;
    private bool _wasSiegeControllerScreenVisible;

    private OrderController PlayerOrderController => this.Mission.PlayerTeam?.PlayerOrderController;

    private SiegeWeaponController PlayerSiegeWeaponController
    {
      get => this.Mission.PlayerTeam?.PlayerOrderController.SiegeWeaponController;
    }

    public MissionAgentLabelView()
    {
      this._agentMeshes = new Dictionary<Agent, MetaMesh>();
      this._labelMaterials = new Dictionary<Texture, Material>();
    }

    public override void OnBehaviorInitialize()
    {
      base.OnBehaviorInitialize();
      this.Mission.Teams.OnPlayerTeamChanged += new Action<Team, Team>(this.Mission_OnPlayerTeamChanged);
      this.Mission.OnMainAgentChanged += new PropertyChangedEventHandler(this.OnMainAgentChanged);
      ManagedOptions.OnManagedOptionChanged += new ManagedOptions.OnManagedOptionChangedDelegate(this.OnManagedOptionChanged);
      this.MissionScreen.OnSpectateAgentFocusIn += new MissionScreen.OnSpectateAgentDelegate(this.HandleSpectateAgentFocusIn);
      this.MissionScreen.OnSpectateAgentFocusOut += new MissionScreen.OnSpectateAgentDelegate(this.HandleSpectateAgentFocusOut);
    }

    public override void AfterStart()
    {
      if (this.PlayerOrderController != null)
      {
        this.PlayerOrderController.OnSelectedFormationsChanged += new Action(this.OrderController_OnSelectedFormationsChanged);
        this.Mission.PlayerTeam.OnFormationsChanged += new Action<Team, Formation>(this.PlayerTeam_OnFormationsChanged);
      }
      BannerBearerLogic missionBehavior = this.Mission.GetMissionBehavior<BannerBearerLogic>();
      if (missionBehavior == null)
        return;
      missionBehavior.OnBannerBearerAgentUpdated += new Action<Agent, bool>(this.BannerBearerLogic_OnBannerBearerAgentUpdated);
    }

    public override void OnMissionTick(float dt)
    {
      bool flag1 = this.IsOrderScreenVisible();
      bool flag2 = this.IsSiegeControllerScreenVisible();
      if (!flag1 && this._wasOrderScreenVisible)
        this.SetHighlightForAgents(false, false, false);
      if (!flag2 && this._wasSiegeControllerScreenVisible)
        this.SetHighlightForAgents(false, true, false);
      if (flag1 && !this._wasOrderScreenVisible)
        this.SetHighlightForAgents(true, false, false);
      if (flag2 && !this._wasSiegeControllerScreenVisible)
        this.SetHighlightForAgents(true, true, false);
      this._wasOrderScreenVisible = flag1;
      this._wasSiegeControllerScreenVisible = flag2;
    }

    public override void OnRemoveBehavior()
    {
      this.UnregisterEvents();
      base.OnRemoveBehavior();
    }

    public override void OnMissionScreenFinalize()
    {
      this.UnregisterEvents();
      base.OnMissionScreenFinalize();
    }

    private void UnregisterEvents()
    {
      if (this.Mission != null)
      {
        this.Mission.Teams.OnPlayerTeamChanged -= new Action<Team, Team>(this.Mission_OnPlayerTeamChanged);
        this.Mission.OnMainAgentChanged -= new PropertyChangedEventHandler(this.OnMainAgentChanged);
      }
      ManagedOptions.OnManagedOptionChanged -= new ManagedOptions.OnManagedOptionChangedDelegate(this.OnManagedOptionChanged);
      if (this.MissionScreen != null)
      {
        this.MissionScreen.OnSpectateAgentFocusIn -= new MissionScreen.OnSpectateAgentDelegate(this.HandleSpectateAgentFocusIn);
        this.MissionScreen.OnSpectateAgentFocusOut -= new MissionScreen.OnSpectateAgentDelegate(this.HandleSpectateAgentFocusOut);
      }
      if (this.PlayerOrderController != null)
      {
        this.PlayerOrderController.OnSelectedFormationsChanged -= new Action(this.OrderController_OnSelectedFormationsChanged);
        if (this.Mission != null)
          this.Mission.PlayerTeam.OnFormationsChanged -= new Action<Team, Formation>(this.PlayerTeam_OnFormationsChanged);
      }
      BannerBearerLogic missionBehavior = this.Mission.GetMissionBehavior<BannerBearerLogic>();
      if (missionBehavior == null)
        return;
      missionBehavior.OnBannerBearerAgentUpdated -= new Action<Agent, bool>(this.BannerBearerLogic_OnBannerBearerAgentUpdated);
    }

    public override void OnAgentRemoved(
      Agent affectedAgent,
      Agent affectorAgent,
      AgentState agentState,
      KillingBlow killingBlow)
    {
      this.RemoveAgentLabel(affectedAgent);
    }

    public override void OnAgentBuild(Agent agent, Banner banner)
    {
      this.InitAgentLabel(agent, banner);
    }

    public override void OnAssignPlayerAsSergeantOfFormation(Agent agent)
    {
      float troopsBannerOpacity = BannerlordConfig.FriendlyTroopsBannerOpacity;
      this._agentMeshes[agent].SetVectorArgument2(30f, 0.4f, 0.44f, 1f * troopsBannerOpacity);
    }

    public override void OnClearScene()
    {
      this._agentMeshes.Clear();
      this._labelMaterials.Clear();
    }

    private void PlayerTeam_OnFormationsChanged(Team team, Formation formation)
    {
      if (!this.IsOrderScreenVisible())
        return;
      this.DehighlightAllAgents();
      this.SetHighlightForAgents(true, false, false);
    }

    private void Mission_OnPlayerTeamChanged(Team previousTeam, Team currentTeam)
    {
      this.DehighlightAllAgents();
      this._wasOrderScreenVisible = false;
      if (previousTeam?.PlayerOrderController != null)
      {
        previousTeam.PlayerOrderController.OnSelectedFormationsChanged -= new Action(this.OrderController_OnSelectedFormationsChanged);
        previousTeam.PlayerOrderController.SiegeWeaponController.OnSelectedSiegeWeaponsChanged -= new Action(this.PlayerSiegeWeaponController_OnSelectedSiegeWeaponsChanged);
      }
      if (this.PlayerOrderController != null)
      {
        this.PlayerOrderController.OnSelectedFormationsChanged += new Action(this.OrderController_OnSelectedFormationsChanged);
        this.PlayerSiegeWeaponController.OnSelectedSiegeWeaponsChanged += new Action(this.PlayerSiegeWeaponController_OnSelectedSiegeWeaponsChanged);
      }
      this.SetHighlightForAgents(true, false, true);
      foreach (Agent agent in (List<Agent>) this.Mission.Agents)
        this.UpdateVisibilityOfAgentMesh(agent);
    }

    private void OrderController_OnSelectedFormationsChanged()
    {
      this.DehighlightAllAgents();
      if (!this.IsOrderScreenVisible())
        return;
      this.SetHighlightForAgents(true, false, false);
    }

    private void PlayerSiegeWeaponController_OnSelectedSiegeWeaponsChanged()
    {
      this.DehighlightAllAgents();
      this.SetHighlightForAgents(true, true, false);
    }

    public void OnAgentListSelectionChanged(bool selectionMode, List<Agent> affectedAgents)
    {
      foreach (Agent affectedAgent in affectedAgents)
      {
        float num = selectionMode ? 1f : -1f;
        if (this._agentMeshes.ContainsKey(affectedAgent))
        {
          MetaMesh agentMesh = this._agentMeshes[affectedAgent];
          float troopsBannerOpacity = BannerlordConfig.FriendlyTroopsBannerOpacity;
          double vectorArgument3 = (double) num * (double) troopsBannerOpacity;
          agentMesh.SetVectorArgument2(30f, 0.4f, 0.44f, (float) vectorArgument3);
        }
      }
    }

    private void BannerBearerLogic_OnBannerBearerAgentUpdated(Agent agent, bool isBannerBearer)
    {
      this.RemoveAgentLabel(agent);
      this.InitAgentLabel(agent);
    }

    private void RemoveAgentLabel(Agent agent)
    {
      if (!agent.IsHuman || !this._agentMeshes.ContainsKey(agent))
        return;
      if ((NativeObject) agent.AgentVisuals != (NativeObject) null)
        agent.AgentVisuals.ReplaceMeshWithMesh(this._agentMeshes[agent], (MetaMesh) null, TaleWorlds.MountAndBlade.BodyMeshTypes.Label);
      this._agentMeshes.Remove(agent);
    }

    private void InitAgentLabel(Agent agent, Banner peerBanner = null)
    {
      if (!agent.IsHuman)
        return;
      Banner banner = peerBanner ?? agent.Origin.Banner;
      if (banner == null)
        return;
      MetaMesh copy = MetaMesh.GetCopy("troop_banner_selection", false, true);
      Material tableauMaterial = Material.GetFromResource("agent_label_with_tableau");
      Texture tableauTextureSmall1 = banner.GetTableauTextureSmall((Action<Texture>) null);
      if (!((NativeObject) copy != (NativeObject) null) || !((NativeObject) tableauMaterial != (NativeObject) null))
        return;
      Texture fromResource = Texture.GetFromResource("banner_top_of_head");
      Material material;
      if (this._labelMaterials.TryGetValue(tableauTextureSmall1 ?? fromResource, out material))
      {
        tableauMaterial = material;
      }
      else
      {
        tableauMaterial = tableauMaterial.CreateCopy();
        Action<Texture> setAction = (Action<Texture>) (tex => tableauMaterial.SetTexture(Material.MBTextureType.DiffuseMap, tex));
        Texture tableauTextureSmall2 = banner.GetTableauTextureSmall(setAction);
        tableauMaterial.SetTexture(Material.MBTextureType.DiffuseMap2, fromResource);
        this._labelMaterials.Add(tableauTextureSmall2, tableauMaterial);
      }
      copy.SetMaterial(tableauMaterial);
      copy.SetVectorArgument(0.5f, 0.5f, 0.25f, 0.25f);
      copy.SetVectorArgument2(30f, 0.4f, 0.44f, -1f);
      agent.AgentVisuals.AddMultiMesh(copy, TaleWorlds.MountAndBlade.BodyMeshTypes.Label);
      this._agentMeshes.Add(agent, copy);
      this.UpdateVisibilityOfAgentMesh(agent);
      this.UpdateSelectionVisibility(agent, this._agentMeshes[agent], new bool?(false));
    }

    private void UpdateVisibilityOfAgentMesh(Agent agent)
    {
      if (!agent.IsActive() || !this._agentMeshes.ContainsKey(agent))
        return;
      bool flag = this.IsMeshVisibleForAgent(agent);
      this._agentMeshes[agent].SetVisibilityMask(flag ? VisibilityMaskFlags.Final : (VisibilityMaskFlags) 0);
    }

    private bool IsMeshVisibleForAgent(Agent agent)
    {
      return this.IsAllyInAllyTeam(agent) && this.MissionScreen.LastFollowedAgent != agent && (double) BannerlordConfig.FriendlyTroopsBannerOpacity > 0.0 && !this.MissionScreen.IsPhotoModeEnabled;
    }

    private void OnUpdateOpacityValueOfAgentMesh(Agent agent)
    {
      if (!agent.IsActive() || !this._agentMeshes.ContainsKey(agent))
        return;
      this._agentMeshes[agent].SetVectorArgument2(30f, 0.4f, 0.44f, -BannerlordConfig.FriendlyTroopsBannerOpacity);
    }

    private bool IsAllyInAllyTeam(Agent agent)
    {
      if (agent?.Team == null || agent == this.Mission.MainAgent)
        return false;
      Team team1 = (Team) null;
      Team team2;
      if (GameNetwork.IsSessionActive)
      {
        team2 = GameNetwork.IsMyPeerReady ? GameNetwork.MyPeer.GetComponent<MissionPeer>().Team : (Team) null;
      }
      else
      {
        team2 = this.Mission.PlayerTeam;
        team1 = this.Mission.PlayerAllyTeam;
      }
      return agent.Team == team2 || agent.Team == team1;
    }

    private void OnMainAgentChanged(object sender, PropertyChangedEventArgs e)
    {
      foreach (Agent key in this._agentMeshes.Keys)
        this.UpdateVisibilityOfAgentMesh(key);
    }

    private void HandleSpectateAgentFocusIn(Agent agent) => this.UpdateVisibilityOfAgentMesh(agent);

    private void HandleSpectateAgentFocusOut(Agent agent)
    {
      this.UpdateVisibilityOfAgentMesh(agent);
    }

    private void OnManagedOptionChanged(ManagedOptions.ManagedOptionsType optionType)
    {
      if (optionType != ManagedOptions.ManagedOptionsType.FriendlyTroopsBannerOpacity)
        return;
      foreach (Agent agent in (List<Agent>) this.Mission.Agents)
      {
        if (agent.IsHuman)
        {
          this.UpdateVisibilityOfAgentMesh(agent);
          if (this.IsMeshVisibleForAgent(agent))
            this.OnUpdateOpacityValueOfAgentMesh(agent);
        }
      }
    }

    private bool IsAgentListeningToOrders(Agent agent)
    {
      if (!this.IsOrderScreenVisible() || agent.Formation == null || !this.IsAllyInAllyTeam(agent))
        return false;
      foreach (Formation selectedFormation in (List<Formation>) this.PlayerOrderController.SelectedFormations)
      {
        if (selectedFormation.HasUnitsWithCondition((Func<Agent, bool>) (unit => unit == agent)))
          return true;
      }
      return false;
    }

    private void UpdateSelectionVisibility(Agent agent, MetaMesh mesh, bool? visibility = null)
    {
      if (!visibility.HasValue)
        visibility = new bool?(this.IsAgentListeningToOrders(agent));
      float num = visibility.Value ? 1f : -1f;
      if (agent.MissionPeer != null)
        return;
      float config = ManagedOptions.GetConfig(ManagedOptions.ManagedOptionsType.FriendlyTroopsBannerOpacity);
      mesh.SetVectorArgument2(30f, 0.4f, 0.44f, num * config);
    }

    private bool IsOrderScreenVisible()
    {
      return this.PlayerOrderController != null && this.MissionScreen.OrderFlag != null && this.MissionScreen.OrderFlag.IsVisible;
    }

    private bool IsSiegeControllerScreenVisible()
    {
      return this.PlayerOrderController != null && this.MissionScreen.OrderFlag != null && this.MissionScreen.OrderFlag.IsVisible;
    }

    private void SetHighlightForAgents(
      bool highlight,
      bool useSiegeMachineUsers,
      bool useAllTeamAgents)
    {
      if (this.PlayerOrderController == null)
        Debug.Print(string.Format("PlayerOrderController is null and playerTeamIsNull: {0}", (object) (this.Mission.PlayerTeam == null)), debugFilter: 17179869184UL);
      if (useSiegeMachineUsers)
      {
        foreach (UsableMachine selectedWeapon in (List<SiegeWeapon>) this.PlayerSiegeWeaponController.SelectedWeapons)
        {
          foreach (UsableMissionObject standingPoint in (List<StandingPoint>) selectedWeapon.StandingPoints)
          {
            Agent userAgent = standingPoint.UserAgent;
            if (userAgent != null)
              this.UpdateSelectionVisibility(userAgent, this._agentMeshes[userAgent], new bool?(highlight));
          }
        }
      }
      else if (useAllTeamAgents)
      {
        if (this.PlayerOrderController.Owner != null)
        {
          Team team = this.PlayerOrderController.Owner.Team;
          if (team == null)
          {
            Debug.Print("PlayerOrderController.Owner.Team is null, overriding with Mission.Current.PlayerTeam", debugFilter: 17179869184UL);
            team = Mission.Current.PlayerTeam;
          }
          foreach (Agent activeAgent in (List<Agent>) team.ActiveAgents)
            this.UpdateSelectionVisibility(activeAgent, this._agentMeshes[activeAgent], new bool?(highlight));
        }
        else
          Debug.Print("PlayerOrderController.Owner is null", debugFilter: 17179869184UL);
      }
      else
      {
        foreach (Formation selectedFormation in (List<Formation>) this.PlayerOrderController.SelectedFormations)
          selectedFormation.ApplyActionOnEachUnit((Action<Agent>) (agent => this.UpdateSelectionVisibility(agent, this._agentMeshes[agent], new bool?(highlight))));
      }
    }

    private void DehighlightAllAgents()
    {
      foreach (KeyValuePair<Agent, MetaMesh> agentMesh in this._agentMeshes)
        this.UpdateSelectionVisibility(agentMesh.Key, agentMesh.Value, new bool?(false));
    }

    public override void OnAgentTeamChanged(Team prevTeam, Team newTeam, Agent agent)
    {
      this.UpdateVisibilityOfAgentMesh(agent);
    }

    public override void OnPhotoModeActivated()
    {
      base.OnPhotoModeActivated();
      foreach (Agent agent in (List<Agent>) this.Mission.Agents)
      {
        if (agent.IsHuman)
          this.UpdateVisibilityOfAgentMesh(agent);
      }
    }

    public override void OnPhotoModeDeactivated()
    {
      base.OnPhotoModeDeactivated();
      foreach (Agent agent in (List<Agent>) this.Mission.Agents)
      {
        if (agent.IsHuman)
          this.UpdateVisibilityOfAgentMesh(agent);
      }
    }
  }
}
